﻿#include "DAWorkFlowExecuter.h"
#include <QMap>
#include <QDebug>
#include <QPointer>
#include "DAWorkFlow.h"

namespace DA
{
class DAWorkFlowExecuterPrivate
{
    DA_IMPL_PUBLIC(DAWorkFlowExecuter)
public:
    DAWorkFlowExecuterPrivate(DAWorkFlowExecuter* p);
    //查找开始节点，根据网结构， 查找到最终的顶层节点_isolatedNodes和_beginNodes通过此函数查找
    void prepareStartExec();

public:
    QPointer< DAWorkFlow > _workflow;
    DAAbstractNode::SharedPointer _startNode;
    /**
     * @brief 记录所有节点执行的入度数量
     *
     * 只有入度数量满足,才能触发节点的出度进行后续的传递，此变量用于解决多从入度和出度下的执行顺序问题
     */
    QMap< DAAbstractNode::SharedPointer, int > _nodeIndegreeSetCount;

    QList< DAAbstractNode::SharedPointer > _isolatedNodes;  ///< 孤立节点
    QList< DAAbstractNode::SharedPointer > _beginNodes;     ///< 开始节点
};
}  // end of namespace DA

//===================================================
// using DA namespace -- 禁止在头文件using！！
//===================================================

using namespace DA;

//===================================================
// DAWorkFlowExecuterPrivate
//===================================================
DAWorkFlowExecuterPrivate::DAWorkFlowExecuterPrivate(DAWorkFlowExecuter* p) : q_ptr(p)
{
}

/**
 * @brief 通过网结构查找顶层
 *
 * - 所有没有入度的节点都认为是顶层
 * - 入度和出度都没有的节点属于孤立节点，孤立节点先执行
 *
 */
void DAWorkFlowExecuterPrivate::prepareStartExec()
{
    //清空记录
    _nodeIndegreeSetCount.clear();
    _isolatedNodes.clear();
    _beginNodes.clear();
    //查找孤立节点和0入度节点
    QList< DAAbstractNode::SharedPointer > nodes = _workflow->nodes();
    for (const DAAbstractNode::SharedPointer& n : qAsConst(nodes)) {
        if (0 == n->getInputNodesCount()) {
            if (0 == n->getOutputNodesCount()) {
                //孤立节点
                _isolatedNodes.append(n);
            } else {
                //否则为开始节点
                _beginNodes.append(n);
            }
        }
    }
}

//====================================
// DAWorkFlowExecuter
//====================================

DAWorkFlowExecuter::DAWorkFlowExecuter(QObject* p) : QObject(p), d_ptr(new DAWorkFlowExecuterPrivate(this))
{
}

DAWorkFlowExecuter::~DAWorkFlowExecuter()
{
}

/**
 * @brief 设置开始节点
 * @param n
 */
void DAWorkFlowExecuter::setStartNode(DAAbstractNode::SharedPointer n)
{
    d_ptr->_startNode = n;
}

/**
 * @brief 设置工作流
 * @param wf
 */
void DAWorkFlowExecuter::setWorkFlow(DAWorkFlow* wf)
{
    d_ptr->_workflow = wf;
}

/**
 * @brief 开始执行节点运算
 *
 * 此函数有DAWorkFlow的exec函数触发
 * @param n
 */
void DAWorkFlowExecuter::startExecute()
{
    d_ptr->prepareStartExec();
    if (d_ptr->_startNode) {
        //如果指定了开始节点，就从开始节点开始执行
        executeNode(d_ptr->_startNode);
    } else {
        //否则自动查找节点开始执行
        //开始执行孤立节点
        for (const DAAbstractNode::SharedPointer& n : qAsConst(d_ptr->_isolatedNodes)) {
            executeNode(n);
        }
        //开始执行开始节点
        for (const DAAbstractNode::SharedPointer& n : qAsConst(d_ptr->_beginNodes)) {
            executeNode(n);
        }
    }
    emit finished(true);
}

/**
 * @brief 节点执行的槽函数
 *
 * 执行完成后会发射 @sa nodeExecuteFinished 信号
 *
 * 关于节点执行等待问题：
 *
 * 如果有如下链路，需要进行等待
 *
 * [1]--->[2.1]--->[2.2]--->[4]
 *    |                    ↑
 *    └-->[3.1]------------┘
 *
 * 上面这个链路1节点分出2.1和3.1
 *
 * 2.1经过2.2才到4，3.1直接到4，因此，4的执行必须等待2.2的执行
 *
 * 复杂一点的情况如：
 *
 *              ┌———>[4.1]—┐
 *              |          ↓
 * [1]--->[2.1]=┙->[2.2]--->[5]
 *    |           ↑        ↑
 *    └-->[3.1]===┙--------┘
 *
 * 也就是说，入度不全，不能继续传递
 *
 * @param n
 */
void DAWorkFlowExecuter::executeNode(DAAbstractNode::SharedPointer n)
{
    qDebug() << tr("execute node, name=") << n->getNodeName() << ",type=" << n->metaData().getNodePrototype();
    bool state = n->exec();
    emit nodeExecuteFinished(n, state);
    //获取节点的输出
    QList< DAAbstractNode::LinkInfo > outInfo = n->getAllOutputLinkInfo();
    //给输出的节点传参数
    for (const DAAbstractNode::LinkInfo& li : qAsConst(outInfo)) {
        QVariant v = n->getOutputData(li.key);
        for (const QPair< QString, DAAbstractNode::SharedPointer >& pair : qAsConst(li.nodes)) {
            pair.second->setInputData(pair.first, v);
            auto ite = d_ptr->_nodeIndegreeSetCount.find(pair.second);
            if (ite == d_ptr->_nodeIndegreeSetCount.end()) {
                ite = d_ptr->_nodeIndegreeSetCount.insert(pair.second, 1);
            } else {
                ++(ite.value());
            }
        }
    }
    //执行输出节点，输出节点的执行要满足入度数量和入度节点链接数量一致才能执行
    for (const DAAbstractNode::LinkInfo& li : qAsConst(outInfo)) {
        for (const QPair< QString, DAAbstractNode::SharedPointer >& pair : qAsConst(li.nodes)) {
            if (pair.second->getInputNodesCount() == d_ptr->_nodeIndegreeSetCount[ pair.second ]) {
                //达成执行条件
                executeNode(pair.second);
            }
        }
    }
}
